Imports BikiSoft.Comm
Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Imports System.Threading

Namespace Comm

    Friend Class Comm_Frame
        Public Enum BikiFrameType As Byte
            ID_CONSULT_PASS = &H91
            ID_CONSULT_INTEGRATED = &H92
            ID_FLASH_DATA = &H80
            eMsgIdLogData = &H81
            ID_EEPROM_DATA = &H82
            ID_FLASH_COPY = &H84
            ID_GENERAL_SPECIFIC = &H55
            ID_LAST_COMMAND_STATUS = &HAA
            ID_RUN_COMMAND = &HE0
            ID_BOOTLOADER = &H70
            ID_ATMEL = &H2A
            ID_PC = &H1A
            ID_ECU = &HA
        End Enum
        Public Enum BikiFrameID As Byte
            ID_UNKNOWN
            ID_ATMEL = &H2A
            ID_PC = &H1A
            ID_ECU = &HA
        End Enum
        Public Enum BikiFrameResponse As Byte
            CMD_OK = &H1
            CMD_FAILED = &H2
            CMD_CRC_FAILED = &H3
            CMD_NEXT = &H4
            SIG_FAILED = &H5
            BUFFER_FULL = &H6
            CMD_INCOMPLETE = &H7
            CMD_UNKNOWN_SENDER = &H8
            CMD_UNKNOWN_RECIVER = &H9
            CMD_BAD_HEADS = &HA
            CMD_UNKNOWN = &H10
            CMD_TOOBIG = &H11
            eCmdFwFailed = &H12
            CMD_TIMEOUT = &H1E
            eCmdSdReadFail = &H22
            CMD_BOOT = &H60
            SEC_BAD_CODE = &H30
            SEC_FUNCTION_LOCKED = &H31
            FLASH_WRITE_TIMEOUT = &H1F
            FLASH_WRITE_FAILED = &H20
            SYNC_FAILED = &HFF
            BOOT_FAILED = &H99
            CMD_PASSED = &HD
            CMD_NOTPASSED = &HF
        End Enum
        Public Enum BikiFrameAction As Byte
            WRITE = &H55
            READ = &HAA
            CMD_STOP = &HB
            CMD_GO = &HC
            RUN_ERASEALL = &H80
            RUN_ERASEBANK1 = &H81
            RUN_ERASEBANK2 = &H82
            RUN_ERASESECTOR = &H88
            RUN_SEC_UNLOCKCODE = &H90
            RUN_SEC_SETPASSWORD = &H92
            RUN_SEC_SETSECLEVEL = &H94
            RUN_GET_INFO = &H70
            RUN_DIAG_RXTX = &H60
            RUN_DIAG_RXCLK = &H61
            RUN_DIAG_FLASH = &H62
            RUN_DIAG_FLASH2 = &H63
            RUN_SET_TRACE_ADR = &H6B
            RUN_TEST_WRITE_PATTERN = 100
            eCmdRunDataLogging = &H6C
            eCmdRunDataLogErase = &H6D
            eCmdRunGetLogReport = &H85
            eCmdRunGetLogInfo = &H86
            eCmdRunSetLogSeek = &H87
            eCmdRunGetLogData = &H89
            eCmdRunGetLogCompData = &H8A
        End Enum
        Public Enum WaitValue As Integer
            WV_TIMEOUT = 258
            WV_LASTCMD = 0
            WV_CRCFAIL = 1
            WV_HEADERFAIL = 2
            WV_SPECIFIC = 3
        End Enum

        Dim m_Frame As Byte() = Nothing
        Dim m_Action As BikiFrameAction = Nothing
        Dim m_Response As BikiFrameType = Nothing

        Sub New(ByVal fType As BikiFrameType, ByVal From As UInt32, ByVal [To] As UInt32, ByVal Length As UInt32)
            Select Case fType
                Case BikiFrameType.ID_FLASH_COPY
                    Dim commandData(10 + (4 * 3)) As Byte

                    commandData(6) = fType
                    Array.Copy(System.BitConverter.GetBytes(From), 0, commandData, 7, 4)
                    Array.Copy(System.BitConverter.GetBytes([To]), 0, commandData, 7 + 4, 4)
                    Array.Copy(System.BitConverter.GetBytes(Length), 0, commandData, 7 + 8, 4)
                    frameHeadFootCrc(commandData)

                    m_Frame = commandData
                    m_Response = BikiFrameType.ID_LAST_COMMAND_STATUS
                Case Else
                    Throw New ArgumentException("Incorrect Initialiser used for type " & System.Enum.GetName(GetType(BikiFrameType), fType))
            End Select
        End Sub

        Sub New(ByVal fType As BikiFrameType, Optional ByVal Address As UInt32 = 0, Optional ByVal Action As BikiFrameAction = Nothing, Optional ByVal Payload As Byte() = Nothing, Optional ByVal Response As BikiFrameType = BikiFrameType.ID_LAST_COMMAND_STATUS)
            Dim arrCmd() As Byte = Nothing
            Select Case fType
                Case BikiFrameType.ID_FLASH_DATA, BikiFrameType.ID_EEPROM_DATA
                    If Action = BikiFrameAction.WRITE Then
                        ReDim arrCmd(UBound(PayLoad) + 17)
                        Array.Copy(PayLoad, 0, arrCmd, 13, PayLoad.Length)
                    ElseIf Action = BikiFrameAction.READ Then
                        ReDim arrCmd(16)
                    End If
                    If Not IsNothing(arrCmd) Then
                        arrCmd(6) = fType
                        arrCmd(7) = Action
                        arrCmd(8) = (Address >> 16) And &HFF
                        arrCmd(9) = (Address >> 8) And &HFF
                        arrCmd(10) = Address And &HFF
                        arrCmd(11) = (PayLoad.Length \ &H100) And &HFF
                        arrCmd(12) = PayLoad.Length And &HFF
                        frameHeadFootCrc(arrCmd)
                    End If
                Case BikiFrameType.ID_RUN_COMMAND
                    Select Case Action
                        Case BikiFrameAction.eCmdRunDataLogging
                            ReDim arrCmd(12)
                            arrCmd(8) = Address
                        Case BikiFrameAction.eCmdRunGetLogInfo, BikiFrameAction.eCmdRunDataLogErase
                            ReDim arrCmd(15)
                            Array.Copy(System.BitConverter.GetBytes(Address), 0, arrCmd, 8, 4)
                        Case BikiFrameAction.RUN_SET_TRACE_ADR
                            ReDim arrCmd(13)
                            arrCmd(8) = (Address) And &HFF
                            arrCmd(9) = (Address >> 8) And &HFF
                        Case BikiFrameAction.RUN_ERASESECTOR
                            ReDim arrCmd(14)
                            arrCmd(8) = (Address >> 16) And &HFF
                            arrCmd(9) = (Address >> 8) And &HFF
                            arrCmd(10) = Address And &HFF
                        Case BikiFrameAction.RUN_SEC_SETPASSWORD, BikiFrameAction.RUN_SEC_SETSECLEVEL, BikiFrameAction.RUN_SEC_UNLOCKCODE
                            If IsNothing(Payload) Then
                                Throw New Exception("Communication Payload expected")
                            Else
                                ReDim arrCmd(11 + Payload.Length)
                                Array.Copy(Payload, 0, arrCmd, 8, Payload.Length)
                            End If
                        Case Else
                            If IsNothing(Payload) Then
                                ReDim arrCmd(11)
                            Else
                                ReDim arrCmd(11 + Payload.Length)
                                Array.Copy(Payload, 0, arrCmd, 8, Payload.Length)
                            End If
                    End Select
                    If Not IsNothing(arrCmd) Then
                        arrCmd(6) = fType
                        arrCmd(7) = Action
                        frameHeadFootCrc(arrCmd)
                    End If
                Case BikiFrameType.ID_CONSULT_INTEGRATED, BikiFrameType.ID_CONSULT_PASS
                    Select Case Action
                        Case BikiFrameAction.CMD_GO, BikiFrameAction.CMD_STOP
                            ReDim arrCmd(11)
                            arrCmd(6) = fType
                            arrCmd(7) = Action
                    End Select
                    frameHeadFootCrc(arrCmd)
                Case BikiFrameType.ID_BOOTLOADER
                    If IsNothing(PayLoad) Then
                        ReDim arrCmd(9)
                        frameHeadFootCrc(arrCmd)
                    Else
                        ReDim arrCmd(UBound(PayLoad) + 10)
                        Array.Copy(PayLoad, 0, arrCmd, 6, PayLoad.Length)
                        frameHeadFootCrc(arrCmd)
                    End If
                    arrCmd(2) = Comm.Comm_Frame.BikiFrameType.ID_BOOTLOADER
                Case BikiFrameType.ID_ECU
                    If Not IsNothing(PayLoad) AndAlso PayLoad.Length >= 2 Then
                        ReDim arrCmd(UBound(PayLoad) + 10)
                        Array.Copy(PayLoad, 0, arrCmd, 6, PayLoad.Length)
                        frameHeadFootCrc(arrCmd)
                        arrCmd(2) = Comm.Comm_Frame.BikiFrameType.ID_ECU
                    End If
            End Select
            m_Action = Action
            m_Frame = arrCmd
            m_Response = Response
        End Sub
        Sub New(ByVal Data() As Byte)
            m_Frame = Data
        End Sub
        Public Function IsValid() As Boolean
            Return crcValidate(m_Frame)
        End Function

        ''' <summary>
        ''' Action for the frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Action() As BikiFrameAction
            Get
                If Not IsNothing(m_Frame) Then
                    Return CType(Me.m_Frame(7), BikiFrameAction)
                End If
            End Get
        End Property
        ''' <summary>
        ''' Excepted response from the frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property Response() As BikiFrameType
            Get
                Return m_Response
            End Get
        End Property

        ''' <summary>
        ''' Actual response in the m_frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property ResponseActual() As BikiFrameResponse
            Get
                If Not IsNothing(m_Frame) Then
                    Return CType(Me.m_Frame(7), BikiFrameResponse)
                End If
            End Get
        End Property

        Public ReadOnly Property FrameType() As BikiFrameType
            Get
                If Not IsNothing(m_Frame) Then
                    Return CType(Me.m_Frame(6), BikiFrameType)
                End If
            End Get
        End Property
        ''' <summary>
        ''' Data for the frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Data() As Byte()
            Get
                Return m_Frame
            End Get
            Set(ByVal value As Byte())
                m_Frame = value
            End Set
        End Property

        Public ReadOnly Property IDFrom() As BikiFrameID
            Get
                If Not IsNothing(m_Frame) Then
                    Return m_Frame(2)
                Else
                    Return BikiFrameID.ID_UNKNOWN
                End If
            End Get
        End Property
        Public ReadOnly Property IDTo() As BikiFrameID
            Get
                If Not IsNothing(m_Frame) Then
                    Return m_Frame(3)
                Else
                    Return BikiFrameID.ID_UNKNOWN
                End If
            End Get
        End Property
        Public ReadOnly Property ID() As BikiFrameType
            Get
                If Not IsNothing(m_Frame) AndAlso m_Frame.Length > 7 Then
                    Return m_Frame(6)
                Else
                    Return Nothing
                End If
            End Get
            'Set(ByVal value As BikiFrameType)
            '    m_Frame(2) = value
            'End Set
        End Property

        ''' <summary>
        ''' Returns the payload part of the frame
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public ReadOnly Property PayLoad(Optional ByVal WithID As Boolean = True) As Byte()
            Get
                Dim f_array() As Byte
                If WithID Then
                    ReDim f_array(m_Frame.Length - 11)
                    Array.Copy(m_Frame, 6, f_array, 0, m_Frame.Length - 10)
                Else
                    ReDim f_array(m_Frame.Length - 12)
                    Array.Copy(m_Frame, 7, f_array, 0, m_Frame.Length - 11)
                End If
                Return f_array
            End Get
        End Property
        Default Public Property Item(ByVal Index As UInt16) As Byte
            Get
                If Index < m_Frame.Length Then
                    Return m_Frame(Index)
                Else
                    Throw New IndexOutOfRangeException("Array Index Out of Bounds")
                End If
            End Get
            Set(ByVal value As Byte)
                m_Frame(Index) = value
            End Set
        End Property

        Private Sub frameHeadFootCrc(ByRef frame As Byte())
            If Not IsNothing(frame) AndAlso frame.Length > 6 Then
                frame(0) = &HAA
                frame(1) = &HA5
                frame(2) = &H2A
                frame(3) = &H1A
                frame(4) = ((frame.Length - 10) \ &H100) And &HFF
                frame(5) = (frame.Length - 10) And &HFF
                frame(UBound(frame) - 1) = &H55
                frame(UBound(frame)) = &H5A
                crcCalculate(frame)
            End If
        End Sub

        ''' <summary>
        ''' This function Calculates the CRC of a BikiUSB packet and places it in the CRC location of the frame.
        ''' </summary>
        ''' <param name="Buffer"></param>
        ''' <remarks></remarks>
        Private Sub crcCalculate(ByRef Buffer As Byte())
            Dim i As Integer
            Dim cr As UShort = 0
            For i = 6 To UBound(Buffer) - 4
                cr = crc(Buffer(i), cr)
            Next
            Buffer(UBound(Buffer) - 3) = (cr \ &H100) And &HFF
            Buffer(UBound(Buffer) - 2) = cr And &HFF
        End Sub
        ''' <summary>
        ''' This function Validates the CRC of a BikiUSB packet
        ''' </summary>
        ''' <param name="Buffer"></param>
        ''' <returns>true is a valid packet</returns>
        ''' <remarks></remarks>
        Private Function crcValidate(ByRef Buffer As Byte()) As Boolean
            Dim i As Integer
            Dim cr As UShort = 0
            For i = 6 To UBound(Buffer) - 4
                cr = crc(Buffer(i), cr)
            Next
            If Buffer(UBound(Buffer) - 3) = ((cr \ &H100) And &HFF) And _
               Buffer(UBound(Buffer) - 2) = (cr And &HFF) _
            Then
                Return True
            Else
                Return False
            End If
        End Function

        Public Shared Function crc(ByVal cd As Byte, ByRef oldCrc As UShort) As UShort
            Dim i As Integer

            oldCrc = oldCrc Xor cd

            For i = 0 To 7
                If (oldCrc And 1) Then
                    oldCrc = (oldCrc \ 2) Xor &HA001
                Else
                    oldCrc = (oldCrc \ 2)
                End If
            Next

            Return oldCrc
        End Function

    End Class
End Namespace